home *** CD-ROM | disk | FTP | other *** search
/ Windows Game Programming for Dummies (2nd Edition) / WinGamProgFD.iso / pc / DirectX SDK / DXSDK / samples / Multimedia / DirectPlay / VoiceClientServer / VoiceClient / voiceclient.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2001-10-31  |  47.3 KB  |  1,235 lines

  1. //----------------------------------------------------------------------------
  2. // File: VoiceClient.cpp
  3. //
  4. // Desc: The VoiceClientServer sample is a simple DirectPlay 
  5. //       voice-based client/server application. 
  6. //
  7. // Copyright (c) 1999-2001 Microsoft Corp. All rights reserved.
  8. //-----------------------------------------------------------------------------
  9. #define STRICT
  10. #include <windows.h>
  11. #include <basetsd.h>
  12. #include <commctrl.h>
  13. #include <cguid.h>
  14. #include <dplay8.h>
  15. #include <dpaddr.h>
  16. #include <dplobby8.h>
  17. #include <dxerr8.h>
  18. #include <tchar.h>
  19. #include "DXUtil.h"
  20. #include "NetVoice.h"
  21. #include "VoiceClientServer.h"
  22. #include "NetClient.h"
  23. #include "resource.h"
  24.  
  25.  
  26.  
  27.  
  28. //-----------------------------------------------------------------------------
  29. // player struct locking defines
  30. //-----------------------------------------------------------------------------
  31. CRITICAL_SECTION g_csPlayerContext;
  32. #define PLAYER_LOCK()                   EnterCriticalSection( &g_csPlayerContext ); 
  33. #define PLAYER_ADDREF( pPlayerInfo )    if( pPlayerInfo ) pPlayerInfo->lRefCount++;
  34. #define PLAYER_RELEASE( pPlayerInfo )   if( pPlayerInfo ) { pPlayerInfo->lRefCount--; if( pPlayerInfo->lRefCount <= 0 ) DestoryPlayerStruct( pPlayerInfo ); } pPlayerInfo = NULL;
  35. #define PLAYER_UNLOCK()                 LeaveCriticalSection( &g_csPlayerContext );
  36.  
  37.  
  38. //-----------------------------------------------------------------------------
  39. // Defines, and constants
  40. //-----------------------------------------------------------------------------
  41. struct APP_PLAYER_INFO
  42. {
  43.     LONG  lRefCount;                        // Ref count so we can cleanup when all threads 
  44.                                             // are done w/ this object
  45.     DPNID dpnidPlayer;                      // DPNID of player
  46.     BOOL  bTalking;                         // Is the player talking
  47.     BOOL  bHalfDuplex;                      // If true, then player cannot talk
  48.     TCHAR strPlayerName[MAX_PLAYER_NAME];   // Player name
  49.  
  50.     APP_PLAYER_INFO* pNext;
  51.     APP_PLAYER_INFO* pPrev;
  52. };
  53.  
  54.  
  55.  
  56.  
  57. //-----------------------------------------------------------------------------
  58. // Global variables
  59. //-----------------------------------------------------------------------------
  60. IDirectPlay8Client*     g_pDPClient                   = NULL;    // DirectPlay peer object
  61. CNetClientWizard*       g_pNetClientWizard            = NULL;    // Connection wizard
  62. IDirectPlay8LobbiedApplication* g_pLobbiedApp         = NULL;    // DirectPlay lobbied app 
  63. CNetVoice*              g_pNetVoice                   = NULL;    // DirectPlay voice helper class
  64. BOOL                    g_bWasLobbyLaunched           = FALSE;   // TRUE if lobby launched
  65. HINSTANCE               g_hInst                       = NULL;    // HINST of app
  66. HWND                    g_hDlg                        = NULL;    // HWND of main dialog
  67. LONG                    g_lNumberOfActivePlayers      = 0;       // Number of players currently in game
  68. DPNID                   g_dpnidLocalPlayer            = 0;
  69. APP_PLAYER_INFO         g_playerHead;
  70. TCHAR                   g_strAppName[256]             = TEXT("VoiceClient");
  71. HRESULT                 g_hrDialog;                              // Exit code for app 
  72. TCHAR                   g_strLocalPlayerName[MAX_PATH];          // Local player name
  73. DVCLIENTCONFIG          g_dvClientConfig;                        // Voice client config
  74. BOOL                    g_bMixingSessionType          = FALSE;   // TRUE if the server is mixing, otherwise its forwarding.
  75.  
  76.  
  77.  
  78.  
  79. //-----------------------------------------------------------------------------
  80. // Function-prototypes
  81. //-----------------------------------------------------------------------------
  82. HRESULT WINAPI   DirectPlayMessageHandler( PVOID pvUserContext, DWORD dwMessageId, PVOID pMsgBuffer );
  83. HRESULT WINAPI   DirectPlayLobbyMessageHandler( PVOID pvUserContext, DWORD dwMessageId, PVOID pMsgBuffer );
  84. HRESULT WINAPI   DirectPlayVoiceClientMessageHandler( PVOID pvUserContext, DWORD dwMessageId, PVOID pMsgBuffer );
  85. INT_PTR CALLBACK SampleDlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam );
  86. INT_PTR CALLBACK VoiceConfigDlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam );
  87. VOID     OnInitDialog( HWND hDlg );
  88. HRESULT  InitDirectPlay();
  89. VOID     AppendTextToEditControl( HWND hDlg, TCHAR* strNewLogLine );
  90. HRESULT  GetPlayerStruct( DPNID dpnidPlayer, APP_PLAYER_INFO** ppPlayerInfo );
  91. VOID     DestoryPlayerStruct( APP_PLAYER_INFO* pPlayerInfo );
  92. VOID     AddPlayerStruct( APP_PLAYER_INFO* pPlayerInfo );
  93. HRESULT  DisplayPlayersInChat( HWND hDlg );
  94. VOID     VoiceConfigDlgOnOK( HWND hDlg );
  95. void     SetPlayerTalking( APP_PLAYER_INFO* pPlayerInfo, BOOL bTalking );
  96.  
  97.  
  98.  
  99.  
  100. //-----------------------------------------------------------------------------
  101. // Name: WinMain()
  102. // Desc: Entry point for the application.  Since we use a simple dialog for 
  103. //       user interaction we don't need to pump messages.
  104. //-----------------------------------------------------------------------------
  105. INT APIENTRY WinMain( HINSTANCE hInst, HINSTANCE hPrevInst, 
  106.                       LPSTR pCmdLine, INT nCmdShow )
  107. {
  108.     HRESULT hr;
  109.     HKEY    hDPlaySampleRegKey;
  110.     BOOL    bConnectSuccess = FALSE;
  111.  
  112.     InitCommonControls();
  113.  
  114.     ZeroMemory( &g_playerHead, sizeof(APP_PLAYER_INFO) );
  115.     g_playerHead.pNext = &g_playerHead;
  116.     g_playerHead.pPrev = &g_playerHead;
  117.  
  118.     g_hInst = hInst; 
  119.     InitializeCriticalSection( &g_csPlayerContext );
  120.  
  121.     // Read persistent state information from registry
  122.     RegCreateKeyEx( HKEY_CURRENT_USER, DPLAY_SAMPLE_KEY, 0, NULL,
  123.                     REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, 
  124.                     &hDPlaySampleRegKey, NULL );
  125.     DXUtil_ReadStringRegKey( hDPlaySampleRegKey, TEXT("Player Name"), 
  126.                              g_strLocalPlayerName, MAX_PATH, TEXT("TestPlayer") );
  127.  
  128.     // Init COM so we can use CoCreateInstance
  129.     CoInitializeEx( NULL, COINIT_MULTITHREADED );
  130.  
  131.     // Create helper class
  132.     g_pNetClientWizard = new CNetClientWizard( hInst, g_strAppName, &g_guidApp );
  133.     g_pNetVoice        = new CNetVoice( DirectPlayVoiceClientMessageHandler, NULL );
  134.  
  135.     if( FAILED( hr = InitDirectPlay() ) )
  136.     {
  137.         DXTRACE_ERR( TEXT("InitDirectPlay"), hr );
  138.         MessageBox( NULL, TEXT("Failed initializing IDirectPlay8Peer. ")
  139.                     TEXT("The sample will now quit."),
  140.                     g_strAppName, MB_OK | MB_ICONERROR );
  141.         return FALSE;
  142.     }
  143.  
  144.     // If we were launched from a lobby client, then we may have connection settings
  145.     // that we can use either host or join a game.  If not, then we'll need to prompt 
  146.     // the user to detrimine how to connect.
  147.     if( g_bWasLobbyLaunched && g_pNetClientWizard->HaveConnectionSettingsFromLobby() )
  148.     {
  149.         // If were lobby launched then the DPL_MSGID_CONNECT has already been
  150.         // handled, and since the lobby client also sent us connection settings
  151.         // we can use them to either host or join a DirectPlay session. 
  152.         if( FAILED( hr = g_pNetClientWizard->ConnectUsingLobbySettings() ) )
  153.         {
  154.             DXTRACE_ERR( TEXT("ConnectUsingLobbySettings"), hr );
  155.             MessageBox( NULL, TEXT("Failed to connect using lobby settings. ")
  156.                         TEXT("The sample will now quit."),
  157.                         g_strAppName, MB_OK | MB_ICONERROR );
  158.  
  159.             bConnectSuccess = FALSE;
  160.         }
  161.         else
  162.         {
  163.             // Read information from g_pNetClientWizard
  164.             _tcscpy( g_strLocalPlayerName, g_pNetClientWizard->GetPlayerName() );
  165.  
  166.             bConnectSuccess = TRUE; 
  167.         }
  168.     }
  169.     else
  170.     {
  171.         // If not lobby launched, prompt the user about the network 
  172.         // connection and which session they would like to join or 
  173.         // if they want to create a new one.
  174.  
  175.         // Setup connection wizard
  176.         g_pNetClientWizard->SetPlayerName( g_strLocalPlayerName );
  177.  
  178.         // Start a connection wizard.  The wizard uses GDI dialog boxes.
  179.         // More complex games can use this as a starting point and add a 
  180.         // fancier graphics layer such as Direct3D.
  181.         hr = g_pNetClientWizard->DoConnectWizard();        
  182.         if( FAILED( hr ) ) 
  183.         {
  184.             DXTRACE_ERR( TEXT("DoConnectWizard"), hr );
  185.             MessageBox( NULL, TEXT("Multiplayer connect failed. ")
  186.                         TEXT("The sample will now quit."),
  187.                         g_strAppName, MB_OK | MB_ICONERROR );
  188.             bConnectSuccess = FALSE;
  189.         } 
  190.         else if( hr == NCW_S_QUIT ) 
  191.         {
  192.             // The user canceled the Multiplayer connect, so quit 
  193.             bConnectSuccess = FALSE;
  194.         }
  195.         else
  196.         {
  197.             bConnectSuccess = TRUE; 
  198.  
  199.             // Read information from g_pNetClientWizard
  200.             _tcscpy( g_strLocalPlayerName, g_pNetClientWizard->GetPlayerName() );
  201.  
  202.             // Write information to the registry
  203.             DXUtil_WriteStringRegKey( hDPlaySampleRegKey, TEXT("Player Name"), g_strLocalPlayerName );
  204.         }
  205.     }
  206.  
  207.     if( bConnectSuccess )
  208.     {
  209.         // Set default DirectPlayVoice setup options
  210.         ZeroMemory( &g_dvClientConfig, sizeof(g_dvClientConfig) );
  211.         g_dvClientConfig.dwSize                 = sizeof(g_dvClientConfig);
  212.         g_dvClientConfig.dwFlags                = DVCLIENTCONFIG_AUTOVOICEACTIVATED |
  213.                                                   DVCLIENTCONFIG_AUTORECORDVOLUME;
  214.         g_dvClientConfig.lPlaybackVolume        = DVPLAYBACKVOLUME_DEFAULT;
  215.         g_dvClientConfig.dwBufferQuality        = DVBUFFERQUALITY_DEFAULT;
  216.         g_dvClientConfig.dwBufferAggressiveness = DVBUFFERAGGRESSIVENESS_DEFAULT;
  217.         g_dvClientConfig.dwThreshold            = DVTHRESHOLD_UNUSED;
  218.         g_dvClientConfig.lRecordVolume          = DVRECORDVOLUME_LAST;
  219.         g_dvClientConfig.dwNotifyPeriod         = 0;
  220.  
  221.         // App is now connected via DirectPlay, so start the game.  
  222.  
  223.         // For this sample, we just start a simple dialog box game.
  224.         g_hrDialog = S_OK;
  225.         DialogBox( hInst, MAKEINTRESOURCE(IDD_MAIN_GAME), NULL, 
  226.                    (DLGPROC) SampleDlgProc );
  227.  
  228.         if( FAILED( g_hrDialog ) )
  229.         {
  230.             if( g_hrDialog == DPNERR_CONNECTIONLOST )
  231.             {
  232.                 MessageBox( NULL, TEXT("The DirectPlay session was lost. ")
  233.                             TEXT("The sample will now quit."),
  234.                             g_strAppName, MB_OK | MB_ICONERROR );
  235.             }
  236.             else
  237.             {
  238.                 DXTRACE_ERR( TEXT("DialogBox"), g_hrDialog );
  239.                 MessageBox( NULL, TEXT("An error occured during the game. ")
  240.                             TEXT("The sample will now quit."),
  241.                             g_strAppName, MB_OK | MB_ICONERROR );
  242.             }
  243.         }
  244.     }
  245.  
  246.     // Disconnect from the DirectPlayVoice session, 
  247.     // and destory it if we are the host player.
  248.     SAFE_DELETE( g_pNetVoice ); 
  249.  
  250.     // Cleanup DirectPlay and helper classes
  251.     if( g_pDPClient )
  252.     {
  253.         g_pDPClient->Close( 0 );
  254.         SAFE_RELEASE( g_pDPClient );
  255.     }
  256.  
  257.     if( g_pLobbiedApp )
  258.     {
  259.         g_pLobbiedApp->Close( 0 );
  260.         SAFE_RELEASE( g_pLobbiedApp );
  261.     }
  262.  
  263.     // Don't delete the wizard until we know that 
  264.     // DirectPlay is out of its message handlers.
  265.     // This will be true after Close() has been called. 
  266.     SAFE_DELETE( g_pNetClientWizard );
  267.  
  268.     RegCloseKey( hDPlaySampleRegKey );
  269.     DeleteCriticalSection( &g_csPlayerContext );
  270.     CoUninitialize();
  271.  
  272.     return TRUE;
  273. }
  274.  
  275.  
  276.  
  277.  
  278. //-----------------------------------------------------------------------------
  279. // Name: InitDirectPlay()
  280. // Desc: 
  281. //-----------------------------------------------------------------------------
  282. HRESULT InitDirectPlay()
  283. {
  284.     DPNHANDLE hLobbyLaunchedConnection = NULL;
  285.     HRESULT hr;
  286.  
  287.     // Create IDirectPlay8Client
  288.     if( FAILED( hr = CoCreateInstance( CLSID_DirectPlay8Client, NULL, 
  289.                                        CLSCTX_INPROC_SERVER,
  290.                                        IID_IDirectPlay8Client, 
  291.                                        (LPVOID*) &g_pDPClient ) ) )
  292.         return DXTRACE_ERR( TEXT("CoCreateInstance"), hr );
  293.  
  294.     // Create IDirectPlay8LobbiedApplication
  295.     if( FAILED( hr = CoCreateInstance( CLSID_DirectPlay8LobbiedApplication, NULL, 
  296.                                        CLSCTX_INPROC_SERVER,
  297.                                        IID_IDirectPlay8LobbiedApplication, 
  298.                                        (LPVOID*) &g_pLobbiedApp ) ) )
  299.         return DXTRACE_ERR( TEXT("CoCreateInstance"), hr );
  300.  
  301.     // Init the helper class, now that g_pDP and g_pLobbiedApp are valid
  302.     g_pNetClientWizard->Init( g_pDPClient, g_pLobbiedApp );
  303.  
  304.     // Init IDirectPlay8Client
  305.     if( FAILED( hr = g_pDPClient->Initialize( NULL, DirectPlayMessageHandler, 0 ) ) )
  306.         return DXTRACE_ERR( TEXT("Initialize"), hr );
  307.  
  308.     // Init IDirectPlay8LobbiedApplication.  Before this Initialize() returns 
  309.     // a DPL_MSGID_CONNECT msg may come in to the DirectPlayLobbyMessageHandler 
  310.     // so be prepared ahead of time.
  311.     if( FAILED( hr = g_pLobbiedApp->Initialize( NULL, DirectPlayLobbyMessageHandler, 
  312.                                                 &hLobbyLaunchedConnection, 0 ) ) )
  313.         return DXTRACE_ERR( TEXT("Initialize"), hr );
  314.  
  315.     // IDirectPlay8LobbiedApplication::Initialize returns a handle to a connnection
  316.     // if we have been lobby launced.  Initialize is guanteeded to return after 
  317.     // the DPL_MSGID_CONNECT msg has been processed.  So unless a we are expected 
  318.     // multiple lobby connections, we do not need to remember the lobby connection
  319.     // handle since it will be recorded upon the DPL_MSGID_CONNECT msg.
  320.     g_bWasLobbyLaunched = ( hLobbyLaunchedConnection != NULL );
  321.  
  322.     return S_OK;
  323. }
  324.  
  325.  
  326.  
  327.  
  328. //-----------------------------------------------------------------------------
  329. // Name: SampleDlgProc()
  330. // Desc: Handles dialog messages
  331. //-----------------------------------------------------------------------------
  332. INT_PTR CALLBACK SampleDlgProc( HWND hDlg, UINT msg, 
  333.                                 WPARAM wParam, LPARAM lParam )
  334. {
  335.     switch( msg ) 
  336.     {
  337.         case WM_INITDIALOG:
  338.         {
  339.             OnInitDialog( hDlg );
  340.             break;
  341.         }
  342.  
  343.         case WM_APP_DISPLAY_PLAYERS:
  344.         {
  345.             DisplayPlayersInChat( hDlg );
  346.             break;
  347.         }
  348.  
  349.         case WM_TIMER:
  350.         {
  351.             DWORD            dwNumPlayers;
  352.             DWORD            iIndex;
  353.             LVITEM           lvItem;
  354.             APP_PLAYER_INFO* pPlayerInfo = NULL;
  355.             HWND             hListView = GetDlgItem( hDlg, IDC_PEOPLE_LIST );
  356.  
  357.             dwNumPlayers = ListView_GetItemCount( hListView );
  358.  
  359.             // Now that they are added and the listview sorted them by name,
  360.             // run through them all caching the listview index with its dpnid
  361.             for( iIndex = 0; iIndex < dwNumPlayers; iIndex++ )
  362.             {
  363.                 HRESULT hr;
  364.                 APP_PLAYER_INFO* pPlayerInfo = NULL;
  365.                 DPNID dpnidPlayer;
  366.  
  367.                 lvItem.mask  = LVIF_PARAM;
  368.                 lvItem.iItem = iIndex;
  369.                 ListView_GetItem( hListView, &lvItem );
  370.  
  371.                 dpnidPlayer = (DPNID) lvItem.lParam;
  372.  
  373.                 PLAYER_LOCK(); // enter player context CS
  374.                 hr = GetPlayerStruct( dpnidPlayer, &pPlayerInfo );
  375.                 PLAYER_ADDREF( pPlayerInfo ); // addref player, since we are using it now
  376.                 PLAYER_UNLOCK(); // leave player context CS
  377.  
  378.                 if( FAILED(hr) || pPlayerInfo == NULL )
  379.                 {
  380.                     // The player who sent this may have gone away before this 
  381.                     // message was handled, so just ignore it
  382.                     continue;
  383.                 }
  384.  
  385.                 TCHAR strStatus[255];
  386.  
  387.                 if( pPlayerInfo->bHalfDuplex )
  388.                 {
  389.                     _tcscpy( strStatus, TEXT("Can't talk") );
  390.                 }
  391.                 else
  392.                 {
  393.                     if( g_bMixingSessionType )
  394.                     {
  395.                         // With mixing servers, you can't tell which
  396.                         // client is talking.
  397.                         _tcscpy( strStatus, TEXT("n/a") );
  398.                     }
  399.                     else
  400.                     {
  401.                         if( pPlayerInfo->bTalking )
  402.                             _tcscpy( strStatus, TEXT("Talking") );
  403.                         else
  404.                             _tcscpy( strStatus, TEXT("Silent") );
  405.                     }
  406.                 }
  407.  
  408.                 lvItem.iItem      = iIndex;
  409.                 lvItem.iSubItem   = 1;
  410.                 lvItem.mask       = LVIF_TEXT;
  411.                 lvItem.pszText    = strStatus;
  412.  
  413.                 PLAYER_LOCK();
  414.                 PLAYER_RELEASE( pPlayerInfo );  // Release player and cleanup if needed
  415.                 PLAYER_UNLOCK();
  416.  
  417.                 SendMessage( hListView, LVM_SETITEM, 0, (LPARAM) &lvItem );
  418.             }
  419.             break;
  420.         }
  421.  
  422.         case WM_COMMAND:
  423.         {
  424.             switch( LOWORD(wParam) )
  425.             {
  426.                 case IDC_SETUP:
  427.                     {
  428.                         // Ask the user for DirectPlayVoice setup params
  429.                         DWORD dwResult = (DWORD)DialogBox( g_hInst, 
  430.                                                            MAKEINTRESOURCE(IDD_VOICE_SETUP), 
  431.                                                            hDlg, (DLGPROC) VoiceConfigDlgProc );
  432.                         if( dwResult != IDCANCEL )
  433.                             g_pNetVoice->ChangeVoiceClientSettings( &g_dvClientConfig );
  434.                     }
  435.                     return TRUE;
  436.  
  437.                 case IDCANCEL:
  438.                     g_hrDialog = S_OK;
  439.                     EndDialog( hDlg, 0 );
  440.                     return TRUE;
  441.             }
  442.             break;
  443.         }
  444.     }
  445.  
  446.     return FALSE; // Didn't handle message
  447. }
  448.  
  449.  
  450.  
  451.  
  452. //-----------------------------------------------------------------------------
  453. // Name: OnInitDialog()
  454. // Desc: Inits the dialog 
  455. //-----------------------------------------------------------------------------
  456. VOID OnInitDialog( HWND hDlg )
  457. {
  458.     LVCOLUMN column;
  459.     RECT     rctListView;
  460.     TCHAR    strHeader[255];
  461.     DWORD    dwVertScrollBar;
  462.     DWORD    dwListViewWidth;
  463.  
  464.     g_hDlg = hDlg;
  465.  
  466.     // Load and set the icon
  467.     HICON hIcon = LoadIcon( g_hInst, MAKEINTRESOURCE( IDI_MAIN ) );
  468.     SendMessage( hDlg, WM_SETICON, ICON_BIG,   (LPARAM) hIcon );  // Set big icon
  469.     SendMessage( hDlg, WM_SETICON, ICON_SMALL, (LPARAM) hIcon );  // Set small icon
  470.  
  471.     SetWindowText( hDlg, g_strAppName );
  472.  
  473.     // Display local player's name
  474.     SetDlgItemText( hDlg, IDC_PLAYER_NAME, g_strLocalPlayerName );
  475.  
  476.     // Setup the listview
  477.     HWND hListView = GetDlgItem( hDlg, IDC_PEOPLE_LIST );
  478.     dwVertScrollBar = GetSystemMetrics( SM_CXVSCROLL );  
  479.     GetClientRect( hListView, &rctListView );
  480.     dwListViewWidth = rctListView.right - dwVertScrollBar;
  481.     column.mask     = LVCF_FMT | LVCF_SUBITEM | LVCF_TEXT | LVCF_WIDTH;
  482.     column.fmt      = LVCFMT_LEFT;
  483.     column.iSubItem = -1;
  484.  
  485.     // Insert the Name column
  486.     _tcscpy( strHeader, TEXT("Name") );
  487.     column.cx         = dwListViewWidth * 5 / 10;
  488.     column.pszText    = strHeader;
  489.     column.cchTextMax = _tcslen( strHeader );
  490.     ListView_InsertColumn( hListView, 0, &column );
  491.  
  492.     // Insert the Status column
  493.     _tcscpy( strHeader, TEXT("Status") );
  494.     column.cx         = dwListViewWidth * 5 / 10;
  495.     column.pszText    = strHeader;
  496.     column.cchTextMax = _tcslen( strHeader );
  497.     ListView_InsertColumn( hListView, 1, &column );
  498.  
  499.     // Connect to the DirectPlay Voice session
  500.     if( FAILED( g_hrDialog = g_pNetVoice->Init( hDlg, FALSE, TRUE,
  501.                                                 g_pDPClient, DVSESSIONTYPE_FORWARDING, 
  502.                                                 NULL, &g_dvClientConfig ) ) )
  503.     {
  504.         if( g_hrDialog == DVERR_USERBACK )
  505.         {
  506.             MessageBox( hDlg, TEXT("The user backed out of the wizard.  ")
  507.                         TEXT("This simple sample does not handle this case, so ")
  508.                         TEXT("the sample will quit."), TEXT("DirectPlay Sample"), MB_OK );
  509.             g_hrDialog = S_OK;
  510.         }
  511.  
  512.         if( g_hrDialog == DVERR_USERCANCEL )
  513.         {
  514.             MessageBox( hDlg, TEXT("The user canceled the wizard. ")
  515.                         TEXT("This simple sample does not handle this case, so ")
  516.                         TEXT("the sample will quit."), TEXT("DirectPlay Sample"), MB_OK );
  517.             g_hrDialog = S_OK;
  518.         }
  519.  
  520.         if( g_hrDialog == DVERR_ALREADYPENDING )
  521.         {
  522.             MessageBox( hDlg, TEXT("Another instance of the Voice Setup Wizard is already running. ")
  523.                         TEXT("This simple sample does not handle this case, so ")
  524.                         TEXT("the sample will quit."), TEXT("DirectPlay Sample"), MB_OK );
  525.             g_hrDialog = S_OK;
  526.         }
  527.  
  528.         if( FAILED(g_hrDialog) ) 
  529.             DXTRACE_ERR( TEXT("Init"), g_hrDialog );
  530.  
  531.         EndDialog( hDlg, 0 );
  532.         return;
  533.     }
  534.  
  535.     // Get the session description to figure out if we are connected to a mixing server
  536.     IDirectPlayVoiceClient* pVoiceClient = g_pNetVoice->GetVoiceClient();
  537.     DVSESSIONDESC dvsd;
  538.     ZeroMemory( &dvsd, sizeof(DVSESSIONDESC) );
  539.     dvsd.dwSize = sizeof(DVSESSIONDESC);
  540.     pVoiceClient->GetSessionDesc( &dvsd );
  541.     g_bMixingSessionType = (dvsd.dwSessionType == DVSESSIONTYPE_MIXING);
  542.  
  543.     // Display a warning to the user if they are in half duplex mode
  544.     if( g_pNetVoice->IsHalfDuplex() ) 
  545.     {
  546.         MessageBox( hDlg, TEXT("You are running in half duplex mode. ")
  547.                     TEXT("In half duplex mode no recording takes place."), 
  548.                     TEXT("DirectPlay Sample"), MB_OK );
  549.     }
  550.  
  551.     // Make a timer to update the listbox 
  552.     // 'Status' column every so often 
  553.     SetTimer( hDlg, 0, 250, NULL );
  554.  
  555.     PostMessage( hDlg, WM_APP_DISPLAY_PLAYERS, 0, 0 );
  556. }
  557.  
  558.  
  559.  
  560.  
  561. //-----------------------------------------------------------------------------
  562. // Name: DisplayPlayersInChat()
  563. // Desc: Displays the active players in the listview
  564. //-----------------------------------------------------------------------------
  565. HRESULT DisplayPlayersInChat( HWND hDlg )
  566. {
  567.     if( hDlg == NULL )
  568.         return S_OK;
  569.  
  570.     LVITEM  lvItem;
  571.     HWND    hListView = GetDlgItem( hDlg, IDC_PEOPLE_LIST );
  572.     TCHAR   strStatus[32];
  573.     TCHAR   strNumberPlayers[32];
  574.     DWORD   dwNumPlayers = 0;
  575.  
  576.     // Remove all the players and re-add them in the player enum callback
  577.     ListView_DeleteAllItems( hListView );
  578.  
  579.     PLAYER_LOCK(); // enter player context CS
  580.  
  581.     APP_PLAYER_INFO* pCurPlayer = g_playerHead.pNext;    
  582.  
  583.     while ( pCurPlayer != &g_playerHead )
  584.     {
  585.         ZeroMemory( &lvItem, sizeof(lvItem) );
  586.  
  587.         // Add the item, saving the player's name and dpnid in the listview
  588.         lvItem.mask       = LVIF_TEXT | LVIF_PARAM;
  589.         lvItem.iItem      = 0;
  590.         lvItem.iSubItem   = 0;
  591.         lvItem.pszText    = pCurPlayer->strPlayerName;
  592.         lvItem.lParam     = (LONG) pCurPlayer->dpnidPlayer;
  593.         lvItem.cchTextMax = _tcslen( pCurPlayer->strPlayerName );
  594.         int nIndex = ListView_InsertItem( hListView, &lvItem );
  595.  
  596.         if( pCurPlayer->bHalfDuplex )
  597.         {
  598.             _tcscpy( strStatus, TEXT("Can't talk") );
  599.         }
  600.         else
  601.         {
  602.             if( g_bMixingSessionType )
  603.             {
  604.                 // With mixing servers, you can't tell which
  605.                 // client is talking.
  606.                 _tcscpy( strStatus, TEXT("n/a") );
  607.             }
  608.             else
  609.             {
  610.                 if( pCurPlayer->bTalking )
  611.                     _tcscpy( strStatus, TEXT("Talking") );
  612.                 else
  613.                     _tcscpy( strStatus, TEXT("Silent") );
  614.             }
  615.         }
  616.  
  617.         // Start the player's status off as silent.  
  618.         lvItem.mask       = LVIF_TEXT;
  619.         lvItem.iItem      = nIndex;
  620.         lvItem.iSubItem   = 1;
  621.         lvItem.pszText    = strStatus;
  622.         lvItem.cchTextMax = _tcslen( strStatus );
  623.         ListView_SetItem( hListView, &lvItem );
  624.  
  625.         dwNumPlayers++;
  626.         pCurPlayer = pCurPlayer->pNext;
  627.     }
  628.  
  629.     PLAYER_UNLOCK(); // leave player context CS
  630.  
  631.     wsprintf( strNumberPlayers, TEXT("%d"), dwNumPlayers );
  632.     SetDlgItemText( hDlg, IDC_NUM_PLAYERS, strNumberPlayers );
  633.  
  634.     return S_OK;
  635. }
  636.  
  637.  
  638.  
  639.  
  640. //-----------------------------------------------------------------------------
  641. // Name: DirectPlayMessageHandler
  642. // Desc: Handler for DirectPlay messages.  This function is called by
  643. //       the DirectPlay message handler pool of threads, so be careful of thread
  644. //       synchronization problems with shared memory
  645. //-----------------------------------------------------------------------------
  646. HRESULT WINAPI DirectPlayMessageHandler( PVOID pvUserContext, 
  647.                                          DWORD dwMessageId, 
  648.                                          PVOID pMsgBuffer )
  649. {
  650.     // Try not to stay in this message handler for too long, otherwise
  651.     // there will be a backlog of data.  The best solution is to 
  652.     // queue data as it comes in, and then handle it on other threads.
  653.     
  654.     // This function is called by the DirectPlay message handler pool of 
  655.     // threads, so be careful of thread synchronization problems with shared memory
  656.  
  657.     switch( dwMessageId )
  658.     {
  659.         case DPN_MSGID_TERMINATE_SESSION:
  660.         {
  661.             PDPNMSG_TERMINATE_SESSION pTerminateSessionMsg;
  662.             pTerminateSessionMsg = (PDPNMSG_TERMINATE_SESSION)pMsgBuffer;
  663.  
  664.             g_hrDialog = DPNERR_CONNECTIONLOST;
  665.             EndDialog( g_hDlg, 0 );
  666.             break;
  667.         }
  668.  
  669.         case DPN_MSGID_RECEIVE:
  670.         {
  671.             HRESULT hr;
  672.             PDPNMSG_RECEIVE pReceiveMsg;
  673.             pReceiveMsg = (PDPNMSG_RECEIVE)pMsgBuffer;
  674.  
  675.             GAMEMSG_GENERIC* pMsg = (GAMEMSG_GENERIC*) pReceiveMsg->pReceiveData;
  676.             switch( pMsg->dwType )
  677.             {
  678.                 case GAME_MSGID_SET_ID:
  679.                 {
  680.                     // The host is tell us the DPNID for this client
  681.                     GAMEMSG_SET_ID* pSetIDMsg;
  682.                     pSetIDMsg = (GAMEMSG_SET_ID*)pReceiveMsg->pReceiveData;
  683.  
  684.                     g_dpnidLocalPlayer = pSetIDMsg->dpnidPlayer;
  685.                     break;
  686.                 }
  687.  
  688.                 case GAME_MSGID_CREATE_PLAYER:
  689.                 {
  690.                     // The host is telling us about a new player 
  691.                     GAMEMSG_CREATE_PLAYER* pCreatePlayerMsg;
  692.                     pCreatePlayerMsg = (GAMEMSG_CREATE_PLAYER*)pReceiveMsg->pReceiveData;
  693.  
  694.                     // Create a new and fill in a APP_PLAYER_INFO
  695.                     APP_PLAYER_INFO* pPlayerInfo = new APP_PLAYER_INFO;
  696.                     ZeroMemory( pPlayerInfo, sizeof(APP_PLAYER_INFO) );
  697.                     pPlayerInfo->lRefCount   = 1;
  698.                     pPlayerInfo->dpnidPlayer = pCreatePlayerMsg->dpnidPlayer;
  699.                     _tcscpy( pPlayerInfo->strPlayerName, pCreatePlayerMsg->strPlayerName );
  700.  
  701.                     PLAYER_LOCK(); // enter player struct CS
  702.                     AddPlayerStruct( pPlayerInfo );
  703.                     PLAYER_UNLOCK(); // leave player struct CS
  704.  
  705.                     // Update the number of active players, and 
  706.                     // post a message to the dialog thread to update the 
  707.                     // UI.  This keeps the DirectPlay message handler 
  708.                     // from blocking
  709.                     InterlockedIncrement( &g_lNumberOfActivePlayers );
  710.  
  711.                     if( g_hDlg != NULL )
  712.                         PostMessage( g_hDlg, WM_APP_DISPLAY_PLAYERS, 0, 0 );
  713.                     break;
  714.                 };
  715.  
  716.                 case GAME_MSGID_DESTROY_PLAYER:
  717.                 {
  718.                     // The host is telling us about a player that's been destroyed
  719.                     APP_PLAYER_INFO*        pPlayerInfo = NULL;
  720.                     GAMEMSG_DESTROY_PLAYER* pDestroyPlayerMsg;
  721.                     pDestroyPlayerMsg = (GAMEMSG_DESTROY_PLAYER*)pReceiveMsg->pReceiveData;
  722.  
  723.                     // Get the player struct accosicated with this DPNID
  724.                     PLAYER_LOCK(); // enter player struct CS
  725.                     hr = GetPlayerStruct( pDestroyPlayerMsg->dpnidPlayer, &pPlayerInfo );
  726.                     PLAYER_UNLOCK(); // leave player struct CS
  727.  
  728.                     if( FAILED(hr) || pPlayerInfo == NULL )
  729.                     {
  730.                         // The player who sent this may have gone away before this 
  731.                         // message was handled, so just ignore it
  732.                         break;
  733.                     }
  734.             
  735.                     // Release the player struct
  736.                     PLAYER_LOCK();                  // enter player struct CS
  737.                     PLAYER_RELEASE( pPlayerInfo );  // Release player and cleanup if needed
  738.                     PLAYER_UNLOCK();                // leave player struct CS
  739.  
  740.                     // Update the number of active players, and 
  741.                     // post a message to the dialog thread to update the 
  742.                     // UI.  This keeps the DirectPlay message handler 
  743.                     // from blocking
  744.                     InterlockedDecrement( &g_lNumberOfActivePlayers );
  745.  
  746.                     if( g_hDlg != NULL )
  747.                         PostMessage( g_hDlg, WM_APP_DISPLAY_PLAYERS, 0, 0 );
  748.                     break;
  749.                 };
  750.             }
  751.  
  752.             break;
  753.         }
  754.     }
  755.  
  756.     // Make sure the DirectPlay MessageHandler calls the CNetConnectWizard handler, 
  757.     // so it can be informed of messages such as DPN_MSGID_ENUM_HOSTS_RESPONSE.
  758.     if( g_pNetClientWizard )
  759.         g_pNetClientWizard->MessageHandler( pvUserContext, dwMessageId, pMsgBuffer );
  760.     
  761.     return S_OK;
  762. }
  763.  
  764.  
  765.  
  766.  
  767. //-----------------------------------------------------------------------------
  768. // Name: AddPlayerStruct()
  769. // Desc: Add pPlayerInfo to the circular linked list, g_playerHead
  770. //-----------------------------------------------------------------------------
  771. VOID AddPlayerStruct( APP_PLAYER_INFO* pPlayerInfo )
  772. {
  773.     pPlayerInfo->pNext = g_playerHead.pNext;
  774.     pPlayerInfo->pPrev = &g_playerHead;
  775.  
  776.     g_playerHead.pNext->pPrev = pPlayerInfo;    
  777.     g_playerHead.pNext = pPlayerInfo;    
  778. }
  779.  
  780.  
  781.  
  782.  
  783. //-----------------------------------------------------------------------------
  784. // Name: DestoryPlayerStruct() 
  785. // Desc: Remove pPlayerInfo from the circular linked list, g_playerHead
  786. //-----------------------------------------------------------------------------
  787. VOID DestoryPlayerStruct( APP_PLAYER_INFO* pPlayerInfo )
  788. {
  789.     pPlayerInfo->pNext->pPrev = pPlayerInfo->pPrev;
  790.     pPlayerInfo->pPrev->pNext = pPlayerInfo->pNext;
  791.  
  792.     SAFE_DELETE( pPlayerInfo );
  793. }
  794.  
  795.  
  796.  
  797.  
  798. //-----------------------------------------------------------------------------
  799. // Name: GetPlayerStruct() 
  800. // Desc: Searchs the circular linked list, g_playerHead, for dpnidPlayer
  801. //-----------------------------------------------------------------------------
  802. HRESULT GetPlayerStruct( DPNID dpnidPlayer, APP_PLAYER_INFO** ppPlayerInfo )
  803. {
  804.     if( ppPlayerInfo == NULL )
  805.         return E_FAIL;
  806.  
  807.     APP_PLAYER_INFO* pCurPlayer = g_playerHead.pNext;
  808.     
  809.     *ppPlayerInfo = NULL;
  810.     while ( pCurPlayer != &g_playerHead )
  811.     {
  812.         if( pCurPlayer->dpnidPlayer == dpnidPlayer )
  813.         {
  814.             *ppPlayerInfo = pCurPlayer;
  815.             return S_OK;
  816.         }
  817.             
  818.         pCurPlayer = pCurPlayer->pNext;
  819.     }
  820.  
  821.     // Not found.
  822.     return E_FAIL;
  823. }
  824.  
  825.  
  826.  
  827.  
  828. //-----------------------------------------------------------------------------
  829. // Name: DirectPlayLobbyMessageHandler
  830. // Desc: Handler for DirectPlay lobby messages.  This function is called by
  831. //       the DirectPlay lobby message handler pool of threads, so be careful of 
  832. //       thread synchronization problems with shared memory
  833. //-----------------------------------------------------------------------------
  834. HRESULT WINAPI DirectPlayLobbyMessageHandler( PVOID pvUserContext, 
  835.                                               DWORD dwMessageId, 
  836.                                               PVOID pMsgBuffer )
  837. {
  838.     switch( dwMessageId )
  839.     {
  840.         case DPL_MSGID_CONNECT:
  841.         {
  842.             PDPL_MESSAGE_CONNECT pConnectMsg;
  843.             pConnectMsg = (PDPL_MESSAGE_CONNECT)pMsgBuffer;
  844.  
  845.             // The CNetConnectWizard will handle this message for us,
  846.             // so there is nothing we need to do here for this simple
  847.             // sample.
  848.             break;
  849.         }
  850.  
  851.         case DPL_MSGID_DISCONNECT:
  852.         {
  853.             PDPL_MESSAGE_DISCONNECT pDisconnectMsg;
  854.             pDisconnectMsg = (PDPL_MESSAGE_DISCONNECT)pMsgBuffer;
  855.  
  856.             // We should free any data associated with the lobby 
  857.             // client here, but there is none.
  858.             break;
  859.         }
  860.  
  861.         case DPL_MSGID_RECEIVE:
  862.         {
  863.             PDPL_MESSAGE_RECEIVE pReceiveMsg;
  864.             pReceiveMsg = (PDPL_MESSAGE_RECEIVE)pMsgBuffer;
  865.  
  866.             // The lobby client sent us data.  This sample doesn't
  867.             // expected data from the client, but it is useful 
  868.             // for more complex apps.
  869.             break;
  870.         }
  871.  
  872.         case DPL_MSGID_CONNECTION_SETTINGS:
  873.         {
  874.             PDPL_MESSAGE_CONNECTION_SETTINGS pConnectionStatusMsg;
  875.             pConnectionStatusMsg = (PDPL_MESSAGE_CONNECTION_SETTINGS)pMsgBuffer;
  876.  
  877.             // The lobby client has changed the connection settings.  
  878.             // This simple sample doesn't handle this, but more complex apps may
  879.             // want to.
  880.             break;
  881.         }
  882.     }
  883.  
  884.     // Make sure the DirectPlay MessageHandler calls the CNetConnectWizard handler, 
  885.     // so the wizard can be informed of lobby messages such as DPL_MSGID_CONNECT
  886.     if( g_pNetClientWizard )
  887.         return g_pNetClientWizard->LobbyMessageHandler( pvUserContext, dwMessageId, 
  888.                                                          pMsgBuffer );
  889.     
  890.     return S_OK;
  891. }
  892.  
  893.  
  894.  
  895.  
  896. //-----------------------------------------------------------------------------
  897. // Name: DirectPlayVoiceClientMessageHandler()
  898. // Desc: The callback for DirectPlayVoice client messages.  
  899. //       This handles client messages and updates the UI the whenever a client 
  900. //       starts or stops talking.  
  901. //-----------------------------------------------------------------------------
  902. HRESULT CALLBACK DirectPlayVoiceClientMessageHandler( LPVOID lpvUserContext, DWORD dwMessageType,
  903.                                                       LPVOID lpMessage )
  904. {
  905.     // Try not to stay in this message handler for too long, otherwise
  906.     // there will be a backlog of data.  The best solution is to 
  907.     // queue data as it comes in, and then handle it on other threads.
  908.     
  909.     // This function is called by the DirectPlay message handler pool of 
  910.     // threads, so be care of thread synchronization problems with shared memory
  911.     HRESULT hr;
  912.     HWND hDlg = (HWND) lpvUserContext;
  913.  
  914.     DPNID dpnidPlayer;
  915.     BOOL bTalking;
  916.  
  917.     switch( dwMessageType )
  918.     {
  919.         case DVMSGID_SESSIONLOST:
  920.             g_hrDialog = DPNERR_CONNECTIONLOST;
  921.             EndDialog( hDlg, 0 );
  922.             break;
  923.  
  924.         case DVMSGID_GAINFOCUS:
  925.         case DVMSGID_LOSTFOCUS:
  926.         {
  927.             TCHAR strWindowName[MAX_PATH];
  928.             wsprintf( strWindowName, TEXT("%s%s"), g_strAppName,
  929.                 (dwMessageType == DVMSGID_LOSTFOCUS) ? TEXT(" (Focus Lost)") : TEXT("") );
  930.  
  931.             SetWindowText( hDlg, strWindowName );
  932.             break;
  933.         }
  934.  
  935.         case DVMSGID_RECORDSTART:             
  936.         { 
  937.             DVMSG_RECORDSTART* pMsg = (DVMSG_RECORDSTART*) lpMessage;
  938.             dpnidPlayer = g_dpnidLocalPlayer;
  939.             bTalking    = TRUE;
  940.             break;
  941.         }
  942.  
  943.         case DVMSGID_RECORDSTOP:             
  944.         {
  945.             DVMSG_RECORDSTOP* pMsg = (DVMSG_RECORDSTOP*) lpMessage;
  946.             dpnidPlayer = g_dpnidLocalPlayer;
  947.             bTalking    = FALSE;
  948.             break;
  949.         }
  950.  
  951.         case DVMSGID_PLAYERVOICESTART:
  952.         {
  953.             DVMSG_PLAYERVOICESTART* pMsg = (DVMSG_PLAYERVOICESTART*) lpMessage;
  954.             dpnidPlayer = pMsg->dvidSourcePlayerID;
  955.             bTalking    = TRUE;
  956.             break;
  957.         }
  958.  
  959.         case DVMSGID_PLAYERVOICESTOP:
  960.         {
  961.             DVMSG_PLAYERVOICESTOP* pMsg = (DVMSG_PLAYERVOICESTOP*) lpMessage;
  962.             dpnidPlayer = pMsg->dvidSourcePlayerID;
  963.             bTalking    = FALSE;
  964.             break;
  965.         }
  966.     }
  967.  
  968.     // With a mixing sever, the client won't recieve DVMSGID_RECORDSTART/STOP msgs.  
  969.     // It will still receieve DVMSGID_PLAYERVOICESTART/STOP messages whenever
  970.     // audio occurs, however the messages do not identify the source.  So
  971.     // in this sample, if the server is in mixing mode then it just reports
  972.     // 'n/a' for the players.  This could be done differently in more complex apps.
  973.     if( g_bMixingSessionType )
  974.         return S_OK;
  975.  
  976.     switch( dwMessageType )
  977.     {
  978.         case DVMSGID_RECORDSTART:             
  979.         case DVMSGID_RECORDSTOP:             
  980.         case DVMSGID_PLAYERVOICESTART:
  981.         case DVMSGID_PLAYERVOICESTOP:
  982.         {
  983.             APP_PLAYER_INFO* pPlayerInfo = NULL;
  984.  
  985.             PLAYER_LOCK(); // enter player context CS
  986.             hr = GetPlayerStruct( dpnidPlayer, &pPlayerInfo );
  987.  
  988.             if( FAILED(hr) || pPlayerInfo == NULL )
  989.             {
  990.                 // The player who sent this may have gone away before this 
  991.                 // message was handled, so just ignore it
  992.                 PLAYER_UNLOCK(); // leave player context CS
  993.                 break;
  994.             }
  995.  
  996.             // Update the talking status
  997.             pPlayerInfo->bTalking = bTalking;     
  998.  
  999.             PLAYER_UNLOCK(); // leave player context CS
  1000.         }
  1001.     }
  1002.  
  1003.     return S_OK;
  1004. }
  1005.  
  1006.  
  1007.  
  1008.  
  1009. //-----------------------------------------------------------------------------
  1010. // Name: VoiceConfigDlgProc()
  1011. // Desc: Prompt the user for DirectPlayVoice setup options
  1012. //-----------------------------------------------------------------------------
  1013. INT_PTR CALLBACK VoiceConfigDlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam )
  1014. {
  1015.     DWORD dwSliderPos;
  1016.  
  1017.     switch( msg ) 
  1018.     {
  1019.         case WM_INITDIALOG:
  1020.             // Set the range on the sliders
  1021.             SendDlgItemMessage( hDlg, IDC_PLAYBACK_SLIDER,       TBM_SETRANGE, FALSE, MAKELONG( 0, 100 ) );
  1022.             SendDlgItemMessage( hDlg, IDC_RECORD_SLIDER,         TBM_SETRANGE, FALSE, MAKELONG( 0, 100 ) );
  1023.             SendDlgItemMessage( hDlg, IDC_QUALITY_SLIDER,        TBM_SETRANGE, FALSE, MAKELONG( DVBUFFERQUALITY_MIN, DVBUFFERQUALITY_MAX ) );
  1024.             SendDlgItemMessage( hDlg, IDC_THRESHOLD_SLIDER,    TBM_SETRANGE, FALSE, MAKELONG( DVTHRESHOLD_MIN,  DVTHRESHOLD_MAX ) );
  1025.             SendDlgItemMessage( hDlg, IDC_AGGRESSIVENESS_SLIDER, TBM_SETRANGE, FALSE, MAKELONG( DVBUFFERAGGRESSIVENESS_MIN, DVBUFFERAGGRESSIVENESS_MAX ) );
  1026.  
  1027.             // Setup the dialog based on the globals 
  1028.  
  1029.             // Set the playback controls
  1030.             if( g_dvClientConfig.lPlaybackVolume == DVPLAYBACKVOLUME_DEFAULT )
  1031.             {
  1032.                 CheckRadioButton( hDlg, IDC_PLAYBACK_DEFAULT, IDC_PLAYBACK_SET, IDC_PLAYBACK_DEFAULT );
  1033.             }
  1034.             else
  1035.             {
  1036.                 dwSliderPos = (DWORD) ( ( g_dvClientConfig.lPlaybackVolume - DSBVOLUME_MIN ) * 
  1037.                                           100.0f / (DSBVOLUME_MAX-DSBVOLUME_MIN) );
  1038.                 CheckRadioButton( hDlg, IDC_PLAYBACK_DEFAULT, IDC_PLAYBACK_SET, IDC_PLAYBACK_SET );
  1039.                 SendDlgItemMessage( hDlg, IDC_PLAYBACK_SLIDER, TBM_SETPOS, TRUE, dwSliderPos );
  1040.             }
  1041.  
  1042.             // Set the record controls
  1043.             if( g_dvClientConfig.dwFlags & DVCLIENTCONFIG_AUTORECORDVOLUME )
  1044.             {
  1045.                 CheckRadioButton( hDlg, IDC_RECORD_DEFAULT, IDC_RECORD_AUTO, IDC_RECORD_AUTO );
  1046.             }
  1047.             else if( g_dvClientConfig.lRecordVolume == DVPLAYBACKVOLUME_DEFAULT )
  1048.             {
  1049.                 CheckRadioButton( hDlg, IDC_RECORD_DEFAULT, IDC_RECORD_AUTO, IDC_RECORD_DEFAULT );
  1050.             }
  1051.             else
  1052.             {
  1053.                 dwSliderPos = (DWORD) ( ( g_dvClientConfig.lRecordVolume - DSBVOLUME_MIN ) * 
  1054.                                           100.0f / (DSBVOLUME_MAX-DSBVOLUME_MIN) );
  1055.                 CheckRadioButton( hDlg, IDC_RECORD_DEFAULT, IDC_RECORD_AUTO, IDC_RECORD_SET );
  1056.                 SendDlgItemMessage( hDlg, IDC_RECORD_SLIDER, TBM_SETPOS, TRUE, dwSliderPos );
  1057.             }
  1058.  
  1059.             // Set the threshold controls
  1060.             if( g_dvClientConfig.dwFlags & DVCLIENTCONFIG_AUTOVOICEACTIVATED )
  1061.             {
  1062.                 CheckRadioButton( hDlg, IDC_THRESHOLD_DEFAULT, IDC_THRESHOLD_AUTO, IDC_THRESHOLD_AUTO );
  1063.             }
  1064.             else if( g_dvClientConfig.dwThreshold == DVTHRESHOLD_DEFAULT )
  1065.             {
  1066.                 CheckRadioButton( hDlg, IDC_THRESHOLD_DEFAULT, IDC_THRESHOLD_AUTO, IDC_THRESHOLD_DEFAULT );
  1067.             }
  1068.             else
  1069.             {
  1070.                 CheckRadioButton( hDlg, IDC_THRESHOLD_DEFAULT, IDC_THRESHOLD_AUTO, IDC_THRESHOLD_SET );
  1071.                 SendDlgItemMessage( hDlg, IDC_THRESHOLD_SLIDER, TBM_SETPOS, TRUE, g_dvClientConfig.dwThreshold );
  1072.             }
  1073.  
  1074.             // Set the quality controls
  1075.             if( g_dvClientConfig.dwBufferQuality == DVBUFFERQUALITY_DEFAULT )
  1076.             {
  1077.                 CheckRadioButton( hDlg, IDC_QUALITY_DEFAULT, IDC_QUALITY_SET, IDC_QUALITY_DEFAULT );
  1078.             }
  1079.             else
  1080.             {
  1081.                 CheckRadioButton( hDlg, IDC_QUALITY_DEFAULT, IDC_QUALITY_SET, IDC_QUALITY_SET );
  1082.                 SendDlgItemMessage( hDlg, IDC_QUALITY_SLIDER, TBM_SETPOS, TRUE, g_dvClientConfig.dwBufferQuality );
  1083.             }
  1084.  
  1085.             // Set the aggressiveness controls
  1086.             if( g_dvClientConfig.dwBufferAggressiveness == DVBUFFERAGGRESSIVENESS_DEFAULT )
  1087.             {
  1088.                 CheckRadioButton( hDlg, IDC_AGGRESSIVENESS_DEFAULT, IDC_AGGRESSIVENESS_SET, IDC_AGGRESSIVENESS_DEFAULT );
  1089.             }
  1090.             else
  1091.             {
  1092.                 CheckRadioButton( hDlg, IDC_AGGRESSIVENESS_DEFAULT, IDC_AGGRESSIVENESS_SET, IDC_AGGRESSIVENESS_SET );
  1093.                 SendDlgItemMessage( hDlg, IDC_AGGRESSIVENESS_SLIDER, TBM_SETPOS, TRUE, g_dvClientConfig.dwBufferAggressiveness );
  1094.             }
  1095.  
  1096.             return TRUE;
  1097.  
  1098.         case WM_NOTIFY:
  1099.             #ifndef NM_RELEASEDCAPTURE
  1100.                 #define NM_RELEASEDCAPTURE (NM_FIRST-16)
  1101.             #endif
  1102.             if( ((LPNMHDR) lParam)->code == NM_RELEASEDCAPTURE )
  1103.             {
  1104.                 // If this is a release capture from a slider, then automatically check 
  1105.                 // its 'Set' radio button.
  1106.                 switch( ((LPNMHDR) lParam)->idFrom )
  1107.                 {
  1108.                 case IDC_PLAYBACK_SLIDER:
  1109.                     CheckRadioButton( hDlg, IDC_PLAYBACK_DEFAULT, IDC_PLAYBACK_SET, IDC_PLAYBACK_SET );
  1110.                     break;
  1111.     
  1112.                 case IDC_RECORD_SLIDER:
  1113.                     CheckRadioButton( hDlg, IDC_RECORD_DEFAULT, IDC_RECORD_AUTO, IDC_RECORD_SET );
  1114.                     break;
  1115.     
  1116.                 case IDC_THRESHOLD_SLIDER:
  1117.                     CheckRadioButton( hDlg, IDC_THRESHOLD_DEFAULT, IDC_THRESHOLD_AUTO, IDC_THRESHOLD_SET );
  1118.                     break;
  1119.     
  1120.                 case IDC_QUALITY_SLIDER:
  1121.                     CheckRadioButton( hDlg, IDC_QUALITY_DEFAULT, IDC_QUALITY_SET, IDC_QUALITY_SET );
  1122.                     break;
  1123.     
  1124.                 case IDC_AGGRESSIVENESS_SLIDER:
  1125.                     CheckRadioButton( hDlg, IDC_AGGRESSIVENESS_DEFAULT, IDC_AGGRESSIVENESS_SET, IDC_AGGRESSIVENESS_SET );
  1126.                     break;
  1127.                 }
  1128.             }
  1129.             return TRUE;            
  1130.  
  1131.         case WM_COMMAND:
  1132.             switch( LOWORD(wParam) )
  1133.             {
  1134.                 case IDOK:
  1135.                     VoiceConfigDlgOnOK( hDlg );
  1136.                     return TRUE;
  1137.  
  1138.                 case IDCANCEL:
  1139.                     EndDialog( hDlg, IDCANCEL );
  1140.                     return TRUE;
  1141.             }
  1142.             break;
  1143.     }
  1144.  
  1145.     return FALSE; // Didn't handle message
  1146. }
  1147.  
  1148.  
  1149.  
  1150.  
  1151. //-----------------------------------------------------------------------------
  1152. // Name: VoiceConfigDlgOnOK()
  1153. // Desc: Figure out all the DirectPlayVoice setup params from the dialog box,
  1154. //       and store them in global vars.
  1155. //-----------------------------------------------------------------------------
  1156. VOID VoiceConfigDlgOnOK( HWND hDlg )
  1157. {
  1158.     DWORD dwSliderPos;
  1159.  
  1160.     g_dvClientConfig.dwFlags = 0;
  1161.  
  1162.     // Figure out the playback params
  1163.     if( IsDlgButtonChecked( hDlg, IDC_PLAYBACK_DEFAULT ) )
  1164.     {
  1165.         g_dvClientConfig.lPlaybackVolume = DVPLAYBACKVOLUME_DEFAULT;
  1166.     }
  1167.     else 
  1168.     {
  1169.         dwSliderPos = (DWORD)SendDlgItemMessage( hDlg, IDC_PLAYBACK_SLIDER, TBM_GETPOS, 0, 0 );
  1170.         g_dvClientConfig.lPlaybackVolume = DSBVOLUME_MIN + (LONG) ( dwSliderPos / 100.0f * 
  1171.                                                                     (DSBVOLUME_MAX-DSBVOLUME_MIN) );
  1172.     }
  1173.  
  1174.     // Figure out the record params
  1175.     if( IsDlgButtonChecked( hDlg, IDC_RECORD_AUTO ) )
  1176.     {
  1177.         g_dvClientConfig.lRecordVolume = 0;
  1178.         g_dvClientConfig.dwFlags       |= DVCLIENTCONFIG_AUTORECORDVOLUME;
  1179.     }
  1180.     else if( IsDlgButtonChecked( hDlg, IDC_RECORD_DEFAULT ) )
  1181.     {
  1182.         g_dvClientConfig.lRecordVolume = DVPLAYBACKVOLUME_DEFAULT;
  1183.     }
  1184.     else 
  1185.     {
  1186.         dwSliderPos = (DWORD)SendDlgItemMessage( hDlg, IDC_RECORD_SLIDER, TBM_GETPOS, 0, 0 );
  1187.         g_dvClientConfig.lRecordVolume = DSBVOLUME_MIN + (LONG) ( dwSliderPos / 100.0f * 
  1188.                                                                   (DSBVOLUME_MAX-DSBVOLUME_MIN) );
  1189.     }
  1190.  
  1191.     // Figure out the threshold params
  1192.     if( IsDlgButtonChecked( hDlg, IDC_THRESHOLD_AUTO ) )
  1193.     {
  1194.         g_dvClientConfig.dwThreshold = DVTHRESHOLD_UNUSED;
  1195.         g_dvClientConfig.dwFlags       |= DVCLIENTCONFIG_AUTOVOICEACTIVATED;
  1196.     }
  1197.     else if( IsDlgButtonChecked( hDlg, IDC_THRESHOLD_DEFAULT ) )
  1198.     {
  1199.         g_dvClientConfig.dwThreshold = DVTHRESHOLD_DEFAULT;
  1200.         g_dvClientConfig.dwFlags       |= DVCLIENTCONFIG_MANUALVOICEACTIVATED;
  1201.     }
  1202.     else 
  1203.     {
  1204.         dwSliderPos = (DWORD)SendDlgItemMessage( hDlg, IDC_THRESHOLD_SLIDER, TBM_GETPOS, 0, 0 );
  1205.         g_dvClientConfig.dwThreshold = dwSliderPos;
  1206.         g_dvClientConfig.dwFlags       |= DVCLIENTCONFIG_MANUALVOICEACTIVATED;
  1207.     }
  1208.  
  1209.     // Figure out the quality params
  1210.     if( IsDlgButtonChecked( hDlg, IDC_QUALITY_DEFAULT ) )
  1211.     {
  1212.         g_dvClientConfig.dwBufferQuality = DVBUFFERQUALITY_DEFAULT;
  1213.     }
  1214.     else 
  1215.     {
  1216.         dwSliderPos = (DWORD)SendDlgItemMessage( hDlg, IDC_QUALITY_SLIDER, TBM_GETPOS, 0, 0 );
  1217.         g_dvClientConfig.dwBufferQuality = dwSliderPos;
  1218.     }
  1219.  
  1220.     // Figure out the aggressiveness params
  1221.     if( IsDlgButtonChecked( hDlg, IDC_AGGRESSIVENESS_DEFAULT ) )
  1222.     {
  1223.         g_dvClientConfig.dwBufferAggressiveness = DVBUFFERAGGRESSIVENESS_DEFAULT;
  1224.     }
  1225.     else 
  1226.     {
  1227.         dwSliderPos = (DWORD)SendDlgItemMessage( hDlg, IDC_AGGRESSIVENESS_SLIDER, TBM_GETPOS, 0, 0 );
  1228.         g_dvClientConfig.dwBufferAggressiveness = dwSliderPos;
  1229.     }
  1230.  
  1231.     EndDialog( hDlg, IDOK );
  1232. }
  1233.  
  1234.  
  1235.